use std::str;
use std::sync::Mutex;
-use core::{Package, Target, PackageId, PackageSet};
+use core::{Package, Target, PackageId, PackageSet, Profile};
use util::{CargoResult, human, Human};
use util::{internal, ChainError, profile};
let id = pkg.package_id().clone();
let all = (id.clone(), pkg_name.clone(), build_state.clone(),
build_output.clone());
- let plugin_deps = super::crawl_build_deps(cx, pkg, target, profile,
- Kind::Host);
+ let plugin_deps = super::load_build_deps(cx, pkg, profile, Kind::Host);
try!(fs::create_dir_all(&cx.layout(pkg, Kind::Target).build(pkg)));
try!(fs::create_dir_all(&cx.layout(pkg, Kind::Host).build(pkg)));
Ok((library_paths, library_links))
}
}
+
+/// Compute the `build_scripts` map in the `Context` which tracks what build
+/// scripts each package depends on.
+///
+/// The global `build_scripts` map lists for all (package, kind) tuples what set
+/// of packages' build script outputs must be considered. For example this lists
+/// all dependencies' `-L` flags which need to be propagated transitively.
+///
+/// The given set of targets to this function is the initial set of
+/// targets/profiles which are being built.
+pub fn build_map(cx: &mut Context, targets: &[(&Target, &Profile)]) {
+ let mut ret = HashMap::new();
+ let pkg = cx.get_package(cx.resolve.root());
+ for &(target, profile) in targets {
+ build(&mut ret, Kind::Target, pkg, target, profile, cx);
+ build(&mut ret, Kind::Host, pkg, target, profile, cx);
+ }
+
+ // Make the output a little more deterministic by sorting all dependencies
+ for (&(id, kind), slot) in ret.iter_mut() {
+ slot.sort_by(|&(p1, _), &(p2, _)| p1.cmp(p2));
+ slot.dedup();
+ debug!("script deps: {}/{:?} => {:?}", id, kind,
+ slot.iter().map(|&(s, _)| s.to_string()).collect::<Vec<_>>());
+ }
+ cx.build_scripts = ret;
+
+ // Recursive function to build up the map we're constructing. This function
+ // memoizes all of its return values as it goes along.
+ fn build<'a, 'b, 'cfg>(out: &'a mut HashMap<(&'b PackageId, Kind),
+ Vec<(&'b PackageId, Profile)>>,
+ kind: Kind,
+ pkg: &'b Package,
+ target: &Target,
+ profile: &Profile,
+ cx: &Context<'b, 'cfg>)
+ -> &'a [(&'b PackageId, Profile)] {
+ // If this target has crossed into "host-land" we need to change the
+ // kind that we're compiling for, and otherwise just do a quick
+ // pre-flight check to see if we've already calculated the set of
+ // dependencies.
+ let kind = if target.for_host() {Kind::Host} else {kind};
+ let id = pkg.package_id();
+ if out.contains_key(&(id, kind)) {
+ return &out[&(id, kind)]
+ }
+
+ // This loop is both the recursive and additive portion of this
+ // function, the key part of the logic being around determining the
+ // right `kind` to recurse on. If a dependency fits in the kind that
+ // we've got specified, then we just keep plazing a trail, but otherwise
+ // we *switch* the kind we're looking at because it must fit into the
+ // other category.
+ //
+ // We always recurse, but only add to our own array if the target is
+ // linkable to us (e.g. not a binary) and it's for the same original
+ // `kind`.
+ let mut ret = Vec::new();
+ for &(pkg, target, p) in cx.dep_targets(pkg, target, profile).iter() {
+ let req = cx.get_requirement(pkg, target);
+
+ let dep_kind = if req.includes(kind) {
+ kind
+ } else if kind == Kind::Target {
+ Kind::Host
+ } else {
+ Kind::Target
+ };
+ let dep_scripts = build(out, dep_kind, pkg, target, p, cx);
+
+ if target.linkable() && kind == dep_kind {
+ if pkg.has_custom_build() {
+ ret.push((pkg.package_id(), profile.clone()));
+ }
+ ret.extend(dep_scripts.iter().cloned());
+ }
+ }
+
+ let prev = out.entry((id, kind)).or_insert(Vec::new());
+ prev.extend(ret);
+ return prev
+ }
+}
let _p = profile::start("preparing build directories");
try!(cx.prepare(pkg, targets));
prepare_init(&mut cx, pkg, &mut queue, &mut HashSet::new());
+ custom_build::build_map(&mut cx, targets);
}
// Build up a list of pending jobs, each of which represent compiling a
let rustcs = try!(prepare_rustc(package, target, profile, crate_types,
cx, req));
- let plugin_deps = crawl_build_deps(cx, package, target, profile, Kind::Host);
+ let plugin_deps = load_build_deps(cx, package, profile, Kind::Host);
return rustcs.into_iter().map(|(mut rustc, kind)| {
let name = package.name().to_string();
let build_state = cx.build_state.clone();
let current_id = package.package_id().clone();
let plugin_deps = plugin_deps.clone();
- let mut native_lib_deps = crawl_build_deps(cx, package, target,
- profile, kind);
+ let mut native_lib_deps = load_build_deps(cx, package, profile, kind);
if package.has_custom_build() && !target.is_custom_build() {
native_lib_deps.insert(0, current_id.clone());
}
}
}
-fn crawl_build_deps<'a>(cx: &'a Context,
- pkg: &'a Package,
- target: &Target,
- profile: &Profile,
- kind: Kind) -> Vec<PackageId> {
- let mut deps = HashSet::new();
- visit(cx, pkg, target, profile, kind, &mut HashSet::new(), &mut deps);
- let mut ret: Vec<_> = deps.into_iter().collect();
- ret.sort();
- return ret;
-
- fn visit<'a>(cx: &'a Context,
- pkg: &'a Package, target: &Target, profile: &Profile,
- kind: Kind,
- visiting: &mut HashSet<&'a PackageId>,
- libs: &mut HashSet<PackageId>) {
- for &(pkg, target, p) in cx.dep_targets(pkg, target, profile).iter() {
- if !target.linkable() { continue }
- let req = cx.get_requirement(pkg, target);
- if !req.includes(kind) { continue }
- if !visiting.insert(pkg.package_id()) { continue }
-
- if pkg.has_custom_build() {
- libs.insert(pkg.package_id().clone());
- }
- visit(cx, pkg, target, p, kind, visiting, libs);
- visiting.remove(&pkg.package_id());
- }
- }
+fn load_build_deps(cx: &Context, pkg: &Package, profile: &Profile,
+ kind: Kind) -> Vec<PackageId> {
+ let pkg = cx.get_package(pkg.package_id());
+ let deps = match cx.build_scripts.get(&(pkg.package_id(), kind)) {
+ Some(a) => a,
+ None => return Vec::new(),
+ };
+ deps.iter().filter(|&&(_, ref dep_profile)| profile == dep_profile)
+ .map(|&(x, _)| x.clone())
+ .collect()
}
// For all plugin dependencies, add their -L paths (now calculated and